/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

import org.jfab.core.inflator.CGSpecInflator;
import org.jfab.core.io.CGWriter;
import org.jfab.core.io.IOWriterException;
import org.jfab.core.io.WriterGroup;
import org.jfab.core.model.CGClass;
import org.jfab.core.model.CGEnum;
import org.jfab.core.model.CGInterface;
import org.jfab.core.model.CGSpec;

/**
 * Provides a Java code fabricator.
 */
public final class JavaFabber implements JFabber
{
    /** Code generator specification inflator. */
    private final CGSpecInflator _cgSpecInflator;

    /** Writer group. */
    private final WriterGroup _writerGroup;

    /**
     * Construct this object with the given parameters.
     * 
     * @param cgSpecInflator Code generator specification inflator.
     * @param writerGroup Writer group.
     */
    public JavaFabber(final CGSpecInflator cgSpecInflator,
            final WriterGroup writerGroup)
    {
        _cgSpecInflator = cgSpecInflator;
        _writerGroup = writerGroup;
    }

    @Override
    public final void generate(final Writer writer, final CGClass cgClass)
    {
        getClassWriter().write(writer, cgClass);
    }

    @Override
    public final void generate(final Writer writer, final CGEnum cgEnum)
    {
        getEnumWriter().write(writer, cgEnum);
    }

    @Override
    public final void generate(final Writer writer,
            final CGInterface cgInterface)
    {
        getInterfaceWriter().write(writer, cgInterface);
    }

    @Override
    public final CGSpec generateCodeFiles(final CGSpec cgSpec,
            final File baseDir)
    {
        String myBaseDir = baseDir.getAbsolutePath();

        if (myBaseDir != null)
        {
            if (!myBaseDir.endsWith("/"))
            {
                myBaseDir = myBaseDir + "/";
            }
        }

        final CGSpec inflatedSpec = getCGSpecInflator().inflate(cgSpec);

        for (final CGInterface cgInterface : inflatedSpec.getInterfaces())
        {
            Writer writer = null;

            try
            {
                final File file = mkdir(myBaseDir,
                        cgInterface.getPackageName(), cgInterface.getName()
                                + ".java");

                if (file == null)
                {
                    throw new IllegalStateException("File is null for "
                            + cgInterface.getName());
                }

                writer = new FileWriter(file);

                generate(writer, cgInterface);
                writer.flush();
                System.out.println("Generated " + file);
            }
            catch (final IOException e)
            {
                throw new IOWriterException(e);
            }
            finally
            {
                if (writer != null)
                {
                    try
                    {
                        writer.flush();
                        writer.close();
                    }
                    catch (final IOException ignore)
                    {
                        // Nothing to do.
                    }
                }
            }
        }

        for (final CGClass cgClass : inflatedSpec.getClasses())
        {
            Writer writer = null;

            try
            {
                final File file = mkdir(myBaseDir, cgClass.getPackageName(),
                        cgClass.getName() + ".java");

                writer = new FileWriter(file);

                generate(writer, cgClass);
                writer.flush();
                System.out.println("Generated " + file);
            }
            catch (final IOException e)
            {
                throw new IOWriterException(e);
            }
            finally
            {
                if (writer != null)
                {
                    try
                    {
                        writer.flush();
                        writer.close();
                    }
                    catch (final IOException ignore)
                    {
                        // Nothing to do.
                    }
                }
            }
        }

        for (final CGEnum cgEnum : inflatedSpec.getEnums())
        {
            Writer writer = null;

            try
            {
                final File file = mkdir(myBaseDir, cgEnum.getPackageName(),
                        cgEnum.getName() + ".java");

                writer = new FileWriter(file);

                generate(writer, cgEnum);
                writer.flush();
                System.out.println("Generated " + file);
            }
            catch (final IOException e)
            {
                throw new IOWriterException(e);
            }
            finally
            {
                if (writer != null)
                {
                    try
                    {
                        writer.flush();
                        writer.close();
                    }
                    catch (final IOException ignore)
                    {
                        // Nothing to do.
                    }
                }
            }
        }

        return inflatedSpec;
    }

    /**
     * @return the cgSpecInflator
     */
    private CGSpecInflator getCGSpecInflator()
    {
        return _cgSpecInflator;
    }

    /**
     * @return the classWriter
     */
    private CGWriter<CGClass> getClassWriter()
    {
        return getWriterGroup().getClassWriter();
    }

    /**
     * @return the enumWriter
     */
    private CGWriter<CGEnum> getEnumWriter()
    {
        return getWriterGroup().getEnumWriter();
    }

    /**
     * @return the interfaceWriter
     */
    private CGWriter<CGInterface> getInterfaceWriter()
    {
        return getWriterGroup().getInterfaceWriter();
    }

    /**
     * @return the writerGroup
     */
    private WriterGroup getWriterGroup()
    {
        return _writerGroup;
    }

    /**
     * Make a directory for the given parameters if it doesn't already exist.
     * 
     * @param baseDir Base directory.
     * @param packageName Package name.
     * @param filename Filename.
     * 
     * @return a file representing the directory.
     */
    private File mkdir(final String baseDir, final String packageName,
            final String filename)
    {
        File answer = null;

        String myBaseDir = baseDir;

        if (packageName != null)
        {
            final String packagePath = packageName.replaceAll("[.]", "/");
            myBaseDir = myBaseDir + packagePath;
        }

        final File dir = new File(myBaseDir);
        answer = new File(dir, filename);

        if (!dir.exists())
        {
            dir.mkdirs();
        }

        return answer;
    }
}
